查看原文
其他

使用Group操作码实现代币化

闪电 黄世亮 闪电HSL 2019-05-08

综述

本文对GROUP属性进行了定义,它包含一个最小32字节的组标识符(group identifier)和一个整数“数量”。这个属性可以定义为一种新交易格式版本里的一个属性,或是通过新操作码(OP_GROUP)执行的部分输出脚本。

Group本质上是建立可互换性壁垒,由共识规则执行:在一笔交易里,不同Group标记的金额不能混合,grouped input的数量必须等于output数量(Group交易余额),除非这笔交易铸币和销毁权限被激活。因此,Group最基本的用途是创建代币。

Group有普通交易和管理操作两种。Group的管理操作是指为Group创建者预留(或指定)的功能,例如,铸币和销毁代币。Group的管理通过交易实现,这些交易实际上“打破”了通常由矿工执行的规则,例如交易Group余额。为了获取这种能力,Group的管理员必须包含一种特殊的UTXO作为交易输入。这些UTXO叫做group authority UTXO。


创建一个Group需要构建一笔交易创建一个group authority UTXO,由此获得所有的管理权限。随后的交易可以花费这个UTXO,再创建其他的group authority UTXO,来执行某些group操作(比如说铸币)。这些group authority UTXO有特殊标注,但在编写脚本/花费时,它们与普通的UTXO是一样的,所以Group的创建者可以将这些UTXO发送到新地址,实现密钥轮换(key rotation),或者将其发送给其他人或多重签名地址进行授权。由于可以创建新的groupauthority UTXO,这些权限可以永久性保留,或者管理员可以通过不再创建新的groupauthority UTXO放弃某一个权限。

本方案引入了脚本模板。脚本模板把兑换脚本里的常量部分和可变部分分离。Group可以执行一项规则,某个grouped output的兑换脚本必须与第一个grouped input的兑换脚本模板相匹配,这是可选的。通过这种方式,只要智能合约的是用比特币脚本语言编写的,代币发行方就可以创建一个由链上“智能合约”管理的代币。



要求

问题陈述

支持SPV钱包

简化的支付确认(简称SPV)技术是使用轻钱包的基础,很多终端用户在手机上使用的就是轻钱包。因为比特现金将来可能会发展到使用非常大的区块,所以个人用户可能无法运行全节点。SPV钱包对于确保人人都可以无需信任、无需许可、免受审查地使用BCH网络来说非常重要。比特现金的目标是成为人人都可以使用的世界货币。而代币非常重要的一个用途是,通过让所有人都可以使用BCH购买代币,推动比特现金的应用和普及。因为SPV钱包是且将会是人们使用BCH网络最常见的方式,我们需要这些钱包支持代币。

类似于比特币的货币属性:无需许可、无法阻挡、匿名、点对点、不可逆、无需信任

比特现金因其独特的属性在世界货币里占据一席之地。基于比特现金的代币也会凭借相同的属性在代币市场里占据一席之地。根据应用不同(股票市场、土地登记等等),通常情况下其他解决方案更为完善和高效。但是类似比特币的独特属性使代币可以为很多使用场景提供解决方案。 

限制发行方的权力,保护所有权

发行方不能因为现有的代币而破坏似比特币的货币属性,除非发行方可以花掉代币。换句话说,区块链必须防止发行方干涉代币转移或持有代币的权利,除非这种特例已编入代币合约。至于其他权力,例如“铸币”(增发),必须可证明(发行方)已放弃这项权利。放弃铸币权才能保证代币限量发行。

区块链必须记录个人持有的代币数量,而不是信任发行方做这件事。这就好比区块链记录BCH的数量。代币的价格可能会因外部因素出现波动(例如,安全性、公众对发行方支持或公司表现的信任度都有可能会影响到价格),但这与外部因素会引起BCH价格波动是一样的。

 

通用的脚本语言

代币和BCH必须使用相同的脚本语言限制output。这样随着脚本语言的发展,代币和BCH都可以得到加强。 

功能描述

创建Group

任何人都可以通过发起交易创建一个特殊输出group authority UTXO,以此创建Group。除了正常的交易手续费外,建Group不需要任何成本。该输出会用32字节的组标识符进行标记,组标识符通过该笔交易的第一个输出(熵源)与任意选择的随机数进行SHA256得到。Group id的最后一个字节是标记字节,用于描述该Group的一些功能。像比特币地址一样,Group的创建者不能选定组标识符,这样不会与现有Group产生冲突,但是创建者可以通过增加随机数(类似于vanity addresses)快速计算多种可能性,来确定一些bits。这样创建者可以找到一个拥有理想标识字节的Group id。

Input:

最多22列

Output:

最多22列

Group标记字节

第32字节的组标识符是标记字节,定义如下:

最多 22 列


附加数据可以放到建Group那笔交易的output OP_RETURN字段。这些信息可以用于为钱包提供更友好的用户界面,用于为用户提供额外信息,例如法律文件。此信息的标准请参见附录C。


Group Authority UTXO

Group Authority UTXO允许交易执行操作,称为权限(capability),共识规则通常是不允许这样操作的。Group Authority UTXO可以用于创建更多的Group AuthorityUTXO。所以,实体有权发送Group Authority UTXO,就有权创建其他的Group Authority UTXO,可以保留自己的权限,或者花掉所有的UTXO,移除自己的权限。

Group authority UTXO可以包含任意的比特币兑换脚本,permission-to-use可以像比特币的permission-to-spend一样变化和有趣。例如,只需要把groupauthority UTXO发送到新地址就可以实现“私钥轮流”(更换Group的管理员),很简单。 

管理权分为不同的权限。从企业的角度来看,比特币钱包要在访问和安全之间权衡。代币发行方的私钥也存在这样的权衡,因为发币和rescripting是最重要的权力。把权力分散,Group权限系统将提供极大的安全性。

目前的权限有:

 

Bit

含义,if 1

63

这是一个authority TXO

62

铸币:创建新的代币。output里的代币数量可以大于input的代币数量,视需要可设置最大值。

61

销毁:销毁代币。Input里的代币数量可以大于output里的代币数量。

60‍

创建子权限:可以创建group authority UTXO。只能创建权限是Group所有input权限相结合的子集的UTXO。也就是说,子UTXO的权限只能是input拥有的权限。

59

Rescript: 如果这个Group需要创建一个output脚本模板(亦称契约,英文covenant),这个权限让你可以创建拥有不同脚本模板的output(描述如下)。

58

Subgroup:已经激活的权限可以应用于已创建的subgroup,并且可以创建新的subgroup。

 



普通的Group output有一个专门指定代币数量的字段。在authority output里,数量字段不是必要的,没有必要再包含上述标记。但是,为便于清楚说明,本文大多数表格/例子的数量和权限/随机数字段都是分开显示。

让我们看一些示例场景(重点在铸币操作):


移除铸币权限

最安全的方案是限制代币的发行量。代币发行方可以花掉所有拥有MINT bit set的group authority UTXO,证明代币的发行量是有限的。假设我们有一个地址bitcoincash:qpae0tg5k4mh5uhhcnwkx2rytj3800j8nunzm8rmed,我们想要创建一个Group限量发行1,000,000个代币。通过两次发币操作,发送100,000个代币到地址1,发送900,000个代币到地址2。

 

创建Group的初始交易(TX0)

Input: 9944d3a522d70d3b0ec4bd395591bd19c5430e6c29e2373a50348f77c9b5b86e,0 (任意UTXO) 

Output: 


创建Group的初始交易会生成一个mint authority UTXO,我们会在下一笔交易中用到这个UTXO。


第一笔铸币交易(TX1):

Input: TX0,0 (上述创建的mintauthority UTXO)

我们通过这笔交易铸了100,000个代币,并又创建了一个mint authority UTXO。

第二笔铸币交易(TX1)

Input: TX1, 1 (第一笔铸币交易创建的mintauthority UTXO) 

Output:

我们通过这笔交易铸了900,000个代币。注意,这笔交易不会再构造mint authority UTXO。mint authority UTXO已用完,无法再创建新的。这证明这个Group实际只有1,000,000个代币。当然,更简单的做法是,用一笔交易两个output创建1,000,000个代币。我们在这里分两笔交易完成是为了演示。

根据不同应用,限制数量的Group可能需要或想要销毁代币或是rescript。通过分权,代币发行方可以在放弃某些权限的同时保留另外一些权限。 

保护铸币权UTXO(Securing Mint Authority UTXO)

但是,保留发币权力是一个常见要求。设想一下,一个代币由某种货币作为担保,需要定期发币/销毁代币,以保证与储备金等值,但这需要极大的安全性。

首先,在公司高层之间创建一个多重签名地址(masterAddr),作为最后的诉求手段。因为这个地址很少会用到,所以可以离线存储和离线签名,非常安全。

创建下一个Group“G”,生成一个具备所有权限的authority UTXO。我们将其称之为master group authority UTXO。在这个场景下,无论我们什么时候花掉这个UTXO,我们都可以创建出另一个拥有相同权限的output,一个未花费一次可以创建出一系列master group authority UTXO。但为了简化概念,我们不会一直对这点进行说明,而是用master group authority UTXO这个术语指代现在的UTXO,即使这个UTXO会多次被花费和重建。

 

无需信任发币

代币公开发行之前(代币获得任意货币支持之前),创建代币可能会是在不安全的环境中完成的。我们假设发行方联系了一个不可信的代理,让其用某些初始配置创建Group。在创建代币期间,这个代理可以执行多次交易,发行方不需要了解或检查这些交易。当创建代币完成,区块链的UTXO集合将会包含所有可用的权限。因此,发行方只需要检查最终的UTXO集合,保证没有不知道的授权即可。发布者是无法隐藏未转播和未确认的授权交易的,因为之前所有的authority UTXO都已经被花出去。因此,他隐藏的交易将一笔毫无价值的双花交易。所以,创建代币无需信任。


密钥轮换(Key Rotation )

一旦创建完成,master group authority UTXO就可以发送到在企业高层之间创建的安全性更高的地址(记为已知地址)。

如有高层出现变动,这个UTXO可以发送到新的多重签名地址,实现“密钥轮换”。

密钥轮换也可以用于其他 authority UTXO。


定期发币

发币时,Group可以设置为定期创建一定数量的代币。要实现这个功能,我们要用mastergroup authority创建一笔交易,生成多个mint authority UTXO。这些authority UTXO无需许可再创建出更多的authority UTXO(CCHILD bit),这样实现一次性授权。数量字段可以进行设置,限制这项一次性授权只能用于创建一定数量的代币。

首先,我们在负责发币的人之间创建一个多重签名地址(mintPubKeys)。接着定义输出脚本。我们会使用用于锁定比特币一段时间的哈希时间锁合约(HTLC)脚本。

def futureSpend(date, minKeys, mintPubKeys...) 

{  

date  

OP_CHECKLOCKTIMEVERIFY  

OP_DROP  

minKeys  

mintPubKeys  

number of mintPubKeys  

OP_CHECKMULTISIG

}


上述用到的比特现金脚本语言简介可参见附录A。

现在让我们来创建这笔交易。


授权定期铸币交易的创建:

Input: 上述创建的master group authority UTXO

例如,提前创建一年份的UTXO,mintPubKeys所有者就可以去处理日常的经营活动。如果出现异常,mintPubKeys所有者仍可以随时再创建新的mint authority UTXO。

如果mintPubKeys被破解,定期发币的特性也可以拖延偷盗者发币的速度,因为特定时间内只能花费一笔UTXO,每笔发币交易创建的时候都限定了发币数量。如果这个代币足够有价值,有必要进行人为干预,一旦UTXO到期(可以花费),公司就可以与盗贼竞争花费这个UTXO,还有可能会获得矿工的支持。

即使认为私钥没有被破解,公司也最好迅速执行发币交易,以使合法交易的速度快于潜在私钥破解者的交易速度。如果公司跑赢私钥破解者,就会发现破解者在网络上双花,同时公司不会有任何资金损失。


可撤销的定期发币

但这些选择并不合适; 我们真的想要一种方法来废除已经被破坏的mint authority UTXO。有两种可能性。

 

通过未确认交易来撤销铸币权限

第一种方法是使用未确认交易。 创建一个哈希时间锁合约(HTLC)和另一个花费少量比特币的UTXO。 使用这些UTXO作为创建铸币权限交易的input。 在这个HTLC可用之前,这个不可转发的交易将会被网络拒绝。 通过这种设置,无效化mintauthority UTXO只需要将input UTXO加倍。 由于这个铸币权限的交易在到期之前是不可转发的,因此网络不会将此视为双重支出。

如果小偷在不被发现的情况下破坏了铸币权限密钥,并且有mint authority creation transaction,则当第一HTLC到期时,通过转发mint authority creation transaction然后用它来铸币,他可以发起一个铸币交易。 一旦在网络上看到该交易,该公司就可以重复花费所有其他mint UTXOs。 

请注意,为了清楚起见,此描述使用3个input(每个input具有独立功能),但应该可以组合这些功能中的任意两个来实现更简单、更有效的应用。

 

通过未确认交易来撤销铸币权限

第二种方法是在mint group authority UTXO中使用“紧急支出”条款。 首先创建一个紧急支出多重签名地址,并将其存储为“masterAddress”。接下来使用以下兑换脚本创建mint group authority UTXO: 

现在,如果出现问题,可以在使用mintSig之前,使用紧急pubkeys来花掉UTXO。 由于在正常操作期间不使用紧急pubkeys,因此可以非常安全地存储它们。并且它们必须安全存储,因为如果被盗的话,小偷就可以花掉所有mint UTXO。 因此,此选项不如第一个选项安全,但可能更容易实现。

OP_IF     

    <date>     

    OP_CHECKLOCKTIMEVERIFY     

    OP_DROP     

    <number of signatures>      

    <pubkeys...>     

    <number of pubkeys>     

    OP_CHECKMULTISIG 

OP_ELSE     

    <number of emergency signatures>     

    <emergency pubkeys...>     

    <number of emergency pubkeys>     

    OP_CHECKMULTISIG  

OP_ENDIF
 

受合约限制的Group(契约)

论文《比特币契约》首次提出一个很强大的概念:限制output脚本为某个特定模板。虽然这项普通功能可以实现各种不同特性不属于本文讨论的内容,但是,契约存在一些关于把input covenant和数量应用到output的实际问题。

如果group拥有这样一种特性:所有的Grouped output都必须匹配一个脚本模板,我们将解决契约的问题,真正在比特现金上实现智能合约。我们的方法是,使用group id里的bit定义受合约限制的group和不受合约限制的Group,以创建一个无需新操作码的脚本模板公式,然后作为一条共识规则,强制Grouped output脚本匹配第一个Grouped input脚本的模板。

 

创建脚本模板A

虽然《比特币契约》提出了一个新的模板对比操作码(template comparison opcode),但是我们可以在不改变比特币脚本的情况下实现契约。首先从审查封装P2PKH(pay to public key hash) 赎回脚本的P2SH(payto script hash)ouput花费开始。我们将其发送至一个由pubkey和addr标记的地址(addr = hash160(pubkey)),sig即签名,像p2pkh(pubkeyhash)和p2sh(scripthash)这样的函数定义大家所熟知的P2PKH和P2SH脚本程序(这些会在下面演示)。我们遵循的脚本规范:将单行数据推送至堆栈,操作码是OP_XXXXX。通过引入宏定义,我们对此进行了扩展,这将会通过替换进行“编译”,类似于C宏。

让我们从输入脚本开始,因为这首先执行:


sig

pubkey

Binary serialization of: p2pkh(addr) 

 

因为无论最后压入什么,都是压入堆栈顶部,所以在看下一步执行输出脚本时别忘了把上面的内容倒过来。

 

def p2sh(scriptHash)

  {

  OP_HASH160

  scriptHash

  OP_EQUAL

  }

 

执行这条指令后,我们已经移去栈顶的项目:Binary serialization of: p2pkh(addr),因此这个堆栈(这里的顶部是真的顶部)看起来会是这样:

 

Pubkey

Sig 


接下来有点尴尬,因为系统“神奇地”知道它应该执行栈顶项目:Binaryserialization of: p2pkh(addr)。P2PKH脚本如下:

 

def p2pkh(pubkeyhash)  

  {  

  OP_DUP  

  OP_HASH160  

  pubkeyhash  

  OP_EQUALVERIFY  

  OP_CHECKSIG  

  } 


这需要公钥和签名,根据OP_CHECKSIG的结果真或假在堆栈上完成脚本的执行。

接下来,我们会用一种类似p2sh的技术创建一个pay-to-template-script-hash(p2tsh) output。我们的方法是把模板“变量”从兑换脚本 (这里我指的是 p2pkh) 里分离出来,将其压入输出脚本的堆栈里。我们就得到了一个脚本哈希值没有变量的p2sh变体。然后我们可以要求交易里的所有grouped inputs 和 outputs,这个脚本哈希值都必须匹配。

除了一个不同的脚本(p2tpkh,描述如下)是序列化的,其他和p2sh是一样的。我们先从input脚本开始,因为它首先执行:

 

sig

pubkey

Redeem script, binary serialization of:p2tpkh(addr) 

 

接下来,输出脚本:


def p2tsh(scriptHash)  

  {  

  // first the standard P2SH, except let’s fix the birthdayattack issue by using 256 bit hashes rather than 160.  

  OP_HASH256  scriptHash  

  OP_EQUAL  

  // next, something new: supply needed parameters to thescript  

pubkeyhash  


像p2sh一样,到这里我们已经移除该脚本并验证其哈希匹配(hash match)。另外,我们已经压入我们的模板参数(公钥哈希值),所以堆栈看起来与p2sh的有所不同:


Pubkeyhash

pubkey

Sig 


接下来执行p2pkh变量的脚本:


def p2tpkh()  

  {  

  OP_SWAP  

  OP_DUP  OP_HASH160  

  OP_EQUALVERIFY  

  OP_CHECKSIG  

  } 

 

注意,这个赎回脚本会拿走其在堆栈上的所有数据。这里没有“写死”的数据值,正如p2pkh脚本里所发现的pubkeyhash。有些堆栈数据是由input脚本提供的,像p2sh一样,有些是由output脚本提供的。因此这个兑换脚本将会作为一个模板。

实现这个“脚本模板”唯一需要改变的是,把这个脚本类型添加到isStandard,对它就像P2SH一样,也就是说需要知道去执行redeem脚本模板。这不是更改共识规则,只是执行兑换脚本的P2SH“魔术”。

 

不合作实体注意事项(Caveats For UncooperativeEntities )

让我们暂时假设Group管理员可以对所有Grouped output执行脚本模板。他可以通过执行一个脚本制定一项政策,部分代币持有者可能不喜欢这项政策并试图推翻它。让我们来看一下,当脚本模板创建者和当前所有者发生分歧时,这个系统会如何运作。以下是一个示例脚本,Group管理员拥有“claw back”的能力:


OP_SWAP

OP_IF  

    p2tpkh()

OP_ELSE   

    OP_DROP 

    p2pkh(ClawBackPubKeyHash)

OP_ENDIF 


这个模板参数是所有者的pubkeyhash。所有者用“ownerPubkey ownerSig 1”进行花费。“管理员的claw back”可以用“clawbackPubkeyclawbackSig 0”花费。但是,新的所有者可以通过把“1pubkeyhash”压入堆栈否决管理员的条款。接着,所有者用“ownerPubkey ownerSig”花费,Group管理员因为“1” causes the OP_IF to be always taken无法花费。本质上,所有者使用的模板参数已经超出“预期”数量,并欺骗脚本用它提供的参数,而不是用来自于花费脚本的参数。

这需要更复杂的脚本,总从堆栈的底部抓取第三个项目。


OP_DEPTH

OP_3

OP_SUB

OP_PICK 

 

OP_IF   

    OP_DROP   

    p2tpkh()

OP_ELSE   

    OP_DROP   

    p2pkh(ClawBackPubKeyHash)

OP_ENDIF 


但是有一个更简单、更好的解决方案,将在为Group引入新交易版本的章节里讨论到。

 

其他的可能性:固定P2SH  

上文提到在P2SH脚本里执行数据的魔术可以清除,这么做可以实现强大的脚本功能。为了实现这点,我们提议执行一个新的操作码OP_EXEC,执行已压入堆栈底部的数据。这样可以解决p2sh问题,P2SH变成:


  OP_DUP

  OP_HASH160

  scriptHash

  OP_EQUAL

  OP_EXEC

 

这将激活MAST(使用签名由代码代替的树签名计划),这方面的内容不在本文的讨论范围内。


最终它将会启用脚本模板,使数据和代码都可以模板化。可以使用非常通用的模板。例如,让我们再看看支持之前定义的允许“claw back”的模板:


OP_SWAP

OP_IF 

        p2tpkh()

OP_ELSE

OP_DROP 

        p2pkh(ClawBackPubKeyHash)

OP_ENDIF 


审查时,模板参数是所有者的pubkeyhash。所有者用“ownerPubkey ownerSig 1”花费,“claw back”可以用“clawbackPubkey clawbackSig 0”花费。但是这个模板有个问题,每一个所有者都必须使用公钥散列机制( public key hash mechanism)限制output。所有者的脚本“部分”被定义为“pay-to-template-public-key-hash”脚本。如果她想要使用多重签名、HTLC或者一些新合约,怎么办?

我们可以使用OP_EXEC实现:


OP_SWAP

OP_IF

OP_EXEC

OP_ELSE

p2pkh(ClawBackPubKeyHash)

OP_ENDIF 


现在模板参数是任意脚本。所有者用"ownerScript 1"花费,“claw back”用相同的“clawbackPubkey clawbackSig 0”花费。

没有限制的OP_EXEC是很危险的。可能会创建消耗大量CPU时间的脚本,导致“拒绝服务攻击”(OP_DUPOP_EXEC,甚至是 recursive trick)。不过我们已经用OP_CAT解决及其他操作码,通过指定限制解决了类似的问题。我认为,即使是严格限制OP_EXEC可以被调用的次数(比如说4次——注意# 指令可以执行的最大次数和堆栈项目数量是有限的),比特币脚本编写者仍然将得到很大好处。


其他的可能性:P2CH

P2SH最初设计的一个目的是将自定义脚本从输出脚本移出,移到花费者(spender)。毕竟,当你发送资金时,你应该不关心接收者接下来会如何花费它。p2tsh的方式则破坏了这个目的,因为这种方式又将脚本移回到了输出脚本。我们可以增加这一功能,使用允许脚本在堆栈中执行两次,和将p2tsh封装进一个p2sh里,来实现这一功能。

但是这个规范使用group提出了一种新的交易版本和格式。在这种情况下,我们可以很好的实现设计——我们将脚本完全移出输出,仅仅包含脚本模板和参数的哈希(详见“交易格式”的章节)。这意味着只有1个地址类型——一个32byte的数字——所以从用户角度来看,最终结果是一个更简洁的系统,但包含了更强大的特性。

 

创建受限制Group

创建受限制的Group的第一步是把它定义成受限制的Group。每一笔交易都必须可以识别这样的Group,而不需要追溯以前的交易。

我们之前定义了组识别符,并展示了如何用一个随机数找出一个最后8bit可以选且可以作为标记字节的Group。我们把标记bit 0定义为Encumbered Group标记。如果这是一个1,那么必须执行下面的共识规则:

 

受限制Group共识规则

1.必须使用p2tsh outputs ( 新交易格式这条永远为真)。

2.如果交易包含附带RESCRIPT field set的group authority input ,则

返回真(不再执行受限规则)

3.对于每一个“真实的”(不是group authority)grouped input:

        a. 验证scriptTemplate的hash256是否匹配已花费UTXO相关的  

           scriptTemplate hash。这个相关scriptTemplate哈希值是包含  

           UTXO交易的第一个“真正”groupedinput的脚本模板哈希值。更多

           细节请参见本文的交易格式部分。


注意,group 里的脚本模板并不一定都是相同的。发行方可以发起一笔铸币交易,模板A在某一个output里,模板B在另外一个output里,或者发行方可以分开发起铸币交易。这样的话,如果代币所有者可以用包含所需模板的UTXO混币,那么他就可以将其所有的代币转换成 encumbrance A或者B。这是可行的,因为encumbrance 实际上是发行方和代币所有者之间的一个合约,发行方可以提供一份新的合约,但持币者有权接受或者拒绝。


子group(Subgroup)

也许有望在一个Group里设置独立的可互换壁垒(fungibility barrier)。我们常见的使用场景是同一类别里不可互换的单品。例如,某一场音乐会的门票,每张票必须指定某个具体的座位。尽管可以创建不同的Group表示每一个座位,但是subgroup可以更好的解决这个问题。

Subgroup具有group的所有属性,但是它是通过32字节group id和附加任意字节形成的(最大是堆栈的上限520字节)。具有subgroup capability set 的group authority可以创建新的subgroup。要创建一个sub-group GrpId.1,就要铸一个代币或者创建一个指定33字节group id的group authority。

普通的subgroup前缀意义不大。换句话说,没有 sub-sub-group。


交易格式

目前的交易格式对于非Group交易来说仍有效。但是,如果Group的交易使用一种新的交易格式,组识别符属性可以从交易output里的脚本中分离出来。虽然牺牲掉了更多变化,但是代码会更简洁。我们在下面提出了一种Group交易必须使用的交易格式:

 

交易格式

Output格式或CTxOut2


注意,ouput里不允许有脚本。对于发送者来说,决定什么spending encumbrances增加到subsequent花费是没有意义的。有些情况下,发送者可能会需要一些限制,接受者可以发送兑换脚本和参数连同兑换脚本哈希值,供发送者验证。因此,我们只需要模板化的比特现金pay-to-script-hash(P2SH)功能模型(一个赎回脚本模板和哈希值和参数)。

redeemScriptHash用于创建地址(例如今天,增加一个typebyte和checksum,通过bech32编码),为了接受付款,用户会互相发送地址。


Input格式或CTxIn2 

没有参数的scriptTemplate是一个正常的脚本。因此如果output没有使用脚本模板,就使用空的“templateParams”和“scriptTemplate”里的正常兑换脚本。

要计算先前输出中指定的“redeemScriptHash”,对连起来的templateParams和scriptTemplate进行hash256(两次sha256)。

如果使用受限制的Group, scriptTemplate hash256必须与COutPoint里指定交易的第一个同组非权限input里的scriptTemplate hash256相匹配。这个句子听起来可能有点让人困惑,初期脚本模板公式使用旧的交易格式,我们在每个output里纳入scriptTemplate哈希值。但是,如上所述,这个公式有一个问题,发送者指那些只对未来花费感兴趣的encumbrance。

scriptTemplate

新的交易格式可以解决这个问题,代价是一个很容易就能获得scriptTemplate哈希值。但是,实现这项检查最简单的方法是,把这个哈希值放回全节点的UTXO集合里。每当创建UTXO时,节点通过验证交易input知道scriptTemplateHash,因此,这个数据可以作为字段添加到每个UTXO里。

另外一个选项是增加两个字段到output格式里:scriptTemplateHash 和parameterHash。但是,这个选择的空间利用率不高,并且还会增加地址的长度。

注意,在新的交易版本里,执行templateParams 脚本的结果变成altstack。这是与我们最初的模板公式有点不同。在我们最初的模板公式里,我们展示了一个不合作用户如何通过推出比预期更多的模板参数打败一个写得很烂的脚本。我们通过使用堆栈操作我们通过使用栈处理指令解决这个问题。但是,还有一个解决方案是,把templateParams放至altstack(执行这个模板时altstack是空的)。templateParams 脚本无法获取花费脚本参数,因为这受限于堆栈压入的数据。当模板脚本运行的时候,它可以轻易通过OP_FROMALTSTACK指令获得模板参数。但是,通过把模板参数从花费参数里分离出来,模板作者的工作将变得容易很多。

 

基本操作


原子交换

我们可以构建一笔交易,将代币转给Alice的同时把BCH转给Bob,这样Alice就可以以一种无须信任、伪匿名和免审查的方式从Bob手中购买代币。如果Alice想要购买1000个T,Bob和Alice必须通过一些可以找到彼此的机制进行沟通,并构建如下交易: 

inputs


半交易

通信协议有可能会使用“半交易(half transaction)”。“半交易”是指一笔因input和output不相抵而无效的签名交易。让我们假设Bob想要以1BCH的价格出售10000个代币T。他构造了下面这笔半交易,发起竞拍:

接着Bob公开发布这笔交易——把十六进制转储的半交易发布到留言板上或是通过邮件发送出去等等。Alice看到Bob发布的信息,根据他的半交易构造如下完整交易:

这笔交易完成就可以发布到BCH网络上。

注意,Bob可以根据他构造的半交易选择发起购买代币或者出售代币的“竞拍”。

这个机制并不是用于取代交易所提供的服务,功能全面的交易所可以快速在链下发起和取消竞拍,还有深度图和诸如止损的功能,等等。但是这个机制的确具备有中心化服务所不具备的特性:交易是伪匿名的,无法停止,点对点,并且无需信任。大部分代币交易可能都不会选择这种方式交易,但是可以以这种方式交易能增强代币持有者对流动性的信心——以“半交易”为基础的代币市场可以并且将会存活下来,无需许可,即使代币没有在交易所上线,或者被下架,也能存活。

 

与 OP_GROUP之间的关系

原OP_GROUP提案受到了若干批评,这项修改后的提案加入了针对这几项批评的解决方案。这部分将探讨这些批评及其解决方案。

 

更简单的共识实现

原OP_GROUP提案里,铸币/销毁的权限由“私钥持有者或者能够满足特定P2SH地址者”授予。采用这种方法的好处在于,概念简单,铸币/销毁的操作效率更高。但是,存在以下几个缺点:1. 授权铸币/销毁代币时,共识代码需要检查之前脚本的内容,以确保这是P2SH或者P2PKH格式,并提取支配地址(controlling address)。脚本模板代码要编入共识。2. 不能更改支配地址(密钥轮换)。3. 不能证明代币的供应量是有限的(注:修改后的OP_GROUP版本已经以一种类似于当前规范的方式解决了这个问题)。

通过分化权限,发行方可以在证明自己已放弃铸币权限的同时保留其他权限。通过授权UTXO而不是私钥,我们密钥轮换,不需要检查脚本的内容,提取共识代码里的支配地址。而只需要做一个小小的检查:mint authorityUTXO是否为拥有CTRL 和MINT bit set的Grouped input。由于大多数权威权力实际上涉及删除约束,因此能力的实施是非常简单的。例如,以下是除了铸币和销毁币功能例外的,强制实施代币平衡的共识代码。 在此代码中,“bal”是通过迭代输入和输出以及代币数量总和而形成的每组数据结构:


if ((bal.input > bal.output) && !hasCapability(bal.ctrlPerms,GroupControllerFlags::MELT))    

{    

return state.Invalid(false,REJECT_GROUP_IMBALANCE, "grp-invalid-melt", "Group input exceedsoutput, but no melt permission");     

}

if ((bal.input < bal.output) && !hasCapability(bal.ctrlPerms,GroupControllerFlags::MINT))     

{     

return state.Invalid(false,REJECT_GROUP_IMBALANCE, "grp-invalid-mint", "Group outputexceeds input, but no mint permission");     

}


尽管共识简化了,但是authority wallet implementation变得更复杂(权力更大),因为代币发行方每次执行管理操作都要消耗authority UTXO,所以钱包必须创建新的authority UTXO。

 

合约Group

原OP_GROUP提案“只能发代币”,不能创建智能合约。这些代币的使用要由链下项目或合约(受信任的实体)管理,在违约的情况下代币持有者只能采取法律行动。

分布式的、无需信任的合约应用(智能合约)使用编程语言定义代币参与者(发行方、代币持有者及其他持有者)之间达成的协议。比特币的脚本语言不能实现这个功能,因为UTXO的发送者可以在每一笔交易里都指定一个新的output脚本。也就是说,持有者只要用不同的脚本给自己发送一笔交易就可以重新定义合约。

我们准备添加 contract encumbered group,以便未来在BCH上实现“智能合约”。现在因脚本无法访问数据而严重受限。几十年来人们都一直认为,数据和代码是开发项目的基本因素,因此,要使用比特现金脚本,获取额外数据是不可避免的。

外部数据可以由oracle签名以及通过OP_DATASIGVERIFY提供。提供交易数据本身很高效,因为每个钱包都肯定保留全部的交易记录用于验证。交易数据实际上已经作为一个固定的OP_CHECKSIG input 提供给脚本,不过因为它没有被压入堆栈,并且已经进行哈希,所以通常是不可用的。

使用外部数据源的例子请参见本文Using Contract Encumbered Groups这一章的内容。为了避免出现先有鸡还是先有蛋的问题,鉴于这些数据访问指令,实现智能合约 contract encumbered group似乎是合理的。

 

Group应用

以下部分给出了一些Groups的用例示例。其实还有更多。但在阅读以下章节之前,您可能需要阅读附录A,其中简要介绍了示例中使用的BchScript语言。另外,如果您正在阅读并问自己“这是否被我的金融监管机构认可?”,请阅读附录B:合法性说明。

 

证券

证券有两种可能的实施方案。首先,证券经纪人持有实际证券的所有权,并在个人投资者交易的区块链上放置一个承诺(代理)代币。这与今天使用的系统非常类似(你实际上并不拥有Schwab,E-trade,Fidelity等代理商的“你的”股票,你拥有期票),并且因为证券代理商在法律上是所有者,它遵守证券法规。

在该实施例中,经纪人有效地将区块链外包给每个人的股票投资组合(如今的内部分类账)的维护,并且外包代表交易所的个人进行交易的需要。当个人想要从该分类帐和代币中插入或提取值时(这与与代币与代币间或代币与BCH间的交易非常不同),会完成KYC / AML。这种KYC / AML-at-the-border战略是当前加密货币交换的工作方式。

第二种实施方案是,公司直接在区块链上发布其自己的证券作为代币。如果这些是注册证券,则区块链分类账是公司的分类账,并且证券的典型要求可以在法律管辖范围内解决,如上文“合法性说明”部分所述。这将公司的股票分类账,官方公告和投票流程放在公共区块链上,这些区块链强制执行某些限制和持有人/所有权,并允许公开验证其余部分。Group管理系统可以用来仔细控制新股票的发行,例如,在董事会成员或公司官员持有的非常安全的多地址中放置无限制的授权,并且只在额外股份获得授权时发行一次性有限数量的授权。

债务证券(债券)可以在到期时使用半交易或通过直接服务进行兑换,该服务可以实现为网站和钱包,其相互作用以创建将代币发送给公司的交易以及自动地向个人发送所需的BCH。

如果您需要出售证券的个人或机构的真实身份,可以索要他们的身份证明。如果向公司注册时使用真实的身份证明才能合法转让,那么当收到证券时,钱包可以自动将此信息直接发送给公司。公共区块链不需要包含或强制执行此信息。

 

抵押贷款或贷款

无论是在个人还是在机构层面,Groups都能实现贷款的简单标记化。在这两种情况下,都会创建一个有限的供应Group来代表贷款。任何所需的额外区块链信息(例如法律合同,担保人,土地等)都放在Group创建事务中的OP_RETURN数据中。如果此贷款不可分割,则使用单个代币创建Group,否则可能会创建许多代币。然后,这些代币可以通过半交易提供给BCH或其他代币,并通过atomic transfer出售。抵押贷款可以通过在单笔交易中包含多个抵押贷款代币来“捆绑”,或者机构可以将一组抵押贷款捆绑在一起并发行另一个代表它们的代币。在第二种情况下,你信任该机构来管理捆绑,但如果捆绑包含链上抵押贷款,你可以验证对这些抵押贷款的利息支付会导致随后向捆绑代币持有人支付正确的价值。与单一抵押贷款一样,OP_RETURN中可以包含指定所涉及的确切抵押以及代币持有人如何获得付款的法律合同(或包括合同的哈希)。

如果你需要抵押贷款人或机构的身份信息,你要向他们询问。而公链不需要包含或者执行该信息。


供应链

供应链查询是指创建一个不可更改的商品生产记录。它实际上是一个有向无环图,因为input产品是通过人、工具和加工成附加产品的过程(这些附加品随后又以类似的方式组合成产品)相结合。因为区块链也是一个有向无环图,UTXO组合形成新的UTXO,所以供应链在区块链上建模很简单。我们还缺失的一环是可互换壁垒,以便把产品区分开来。Group正好可以补上这缺失的一环。

让我们模拟一下这样的映射:

从原材料到成品的过程中需要使用到各种不同的“原料”:

    1.制成最终产品所需的材料/产品

    2.消耗掉的材料/产品

    3.“催化”材料/产品、工具、人力

    4.加工流程

    5.测试


从第1、2、3步就可以看出,这是循环的——某一个流程里的output产品,也许是另一个流程里的input。

原材料和产品展示是通过建group来创建的。对于不可互换的产品例如钻石,可以是一个单独的代币group(比如说,我们切割一个钻石,就创建GroupD来追踪它)。可互换的或者“大部分”可互换的(可互换但你需要追踪个体)产品/材料可以每个类型用一个Group来建模,把一些代币发送到一个UTXO用来代表一个实例。例如,Acme 挖出来的1克金弹头可以是GroupG, 每次制造出一个弹头,就往新的UTXO(1)发送1000个代币,然后在OP_RETURN来填写制造信息。为了标记化,我们用dotted convention来指代Group和initial UTXO instance,括号里是数量(如果相关的话)。所以,这个金弹头是G.1(1000)。为了补充完整这个例子,我们用M.1(10) 指代一个可以使用10次的模具,Rings公司的一个员工定义为E.1。当Rings公司购买这些原材料产品,代表这些原材料的代币将会转至购买者。

衍生的产品可以建模为交易。为了制造出500毫克的金戒指,我们构造一笔交易把G.1(1000)分成两个G.1(500)output。接着,我们再创建一笔交易,把所有的input放到一起造成一枚特殊戒指:D、G.1(500)、M.1(1)、E.1并支付,把它们发送到R的地址R 。实际上打造一枚特殊戒指的成分由R可花费的UTXO表示。R实际上成了这枚戒指的识别符。

注意,如果黄金贬值或者重量少于500毫克,该公司可以用多出来的黄金制造出更多产品,因为公司将会用完G代币。个别的黄金贬值需要黄金制造公司协助,将被盗黄金引入供应链。同样的。公司不能引入不一致的钻石到供应链上,因为该公司已经创建钻石代币,但是这会被注意到的,因为该公司没有钻石矿。戒指打得不好?模具只能用10次,有10个代币可以保证Rings公司使用这个模具不超过10次,或者已经生产出来的戒指不会有模具代币作为input。因为M.1与这枚锻造得不好的戒指有关,我们可以把这个数据与其他锻造得不好的数据相结合,从统计数据上看,这枚戒指质量不好是不是因为某个模具或是模具制造商造成。最后,雇员E.1被定义为生产者,所以人为失误或者偷盗行为也可以追踪到个人。

所以,在区块链上,产品就是关联到一个特定地址,理想状态下包含一笔“初始”交易的完整UTXO集合。但是如果一个产品有成千上万配件的产品,其本身也是另外一个产品的配件,这会变得很庞大复杂。为了解决这个问题,我们创建一个与这个中间产品相关的Group P。发行代币P.1并将其发送到与其他所有配件相同的地址,最好是这个产品创始交易的一部分。当产品卖出,代币P.1就会发送给购买者,而不是其所有配件部分的UTXO。这部分UTXO现在可以销毁,以减少UTXO的消耗。这样执行可以很高效,因为melt authority可以提供给这家公司,因为它与铸币权限是独立的。但是,因为钻石一般被用来打造新的戒指,当戒指被拆解时,钻石UTXO可以选择不销毁。

即使我们已经将大量成员打包进一个代币P.1。全节点仍然可以完整的供应链。如果产品是通过一笔交易创建的,SPV客户端可以请求查看P1代币input交易和SPVproof迅速查看P.1的组成部分。如果产品是通过多笔交易产生,SPV钱包首先必须找到P.1代币之前的地址,然后请求所有这笔地址相关的UTXO。这并不繁琐;因为用备份种子恢复的时候需要恢复所有的交易记录,所以这项功能在作为区块浏览器和轻钱包的全节点中已经很常见了。

这个系统的第一步,创建原材料代币,需要信任。我们要把现有这种材料的数据输入区块链,这也一直需要信任。但之后,这个系统就无需信任了。


票证

通票,即不需要指定座位的票,可以用代币来表示,为该活动创建Group,一个代币就是一张票。售票相当于一笔BCH原子交易,钱包可以用手机的NFC功能证明所有权,或者打印二维码。

很多场馆都不鼓励转售,而这可以通过encumbered group实现。但是可能会难以区分这是个人之间的换票,还是转售。

指定座位的票可以通过subgroup编码座位信息来实现。每个座位都是其自己的Group,因而是不可互换的。

总的来说,对于大型、不支持预售的活动来说,使用区块存储票据并不是最好的选择,中心化的服务器和手机APP支持BCH购票,不过执行的售票政策是与区块链的理念是相反的。但是,基于区块链的售票方式对于当地每年数百万场的活动来说是一个简单有效的方案。没有预算或者制定售票方案的专业知识,这些票的功能:发行、销售、预售、防伪,还有入场全部都可以通过一个手机钱包完成。

 

使用合约受限制的Group

比特币的脚本语言严格限制对数据的访问。输出脚本只能在通过花费(签名)脚本提供给它的数据上运行。但花费脚本的创建者会被激励,提供任意满足输出脚本的数据(真或假)。

多年来,代码和数据都被认为是程序的基本组成部分(https://en.wikipedia.org/wiki/Algorithms_%2B_Data_Structures_%3D_Programs) 。没有数据,现在能用的脚本一只手就能数得过来。

如果我们想要把比特币脚本变成一个实用的合约语言,让输出脚本可以访问更多的数据源是很有必要的。有些额外的数据源是可以以一种无须信任的方式提供的,例如关于交易本身的信息,或者以一种部分可信的方式提供,例如OP_DATASIGVERIFY操作码的output, 该操作码暂定于11月激活。

比特现金网络通过一次分叉就部署大量更改是不现实的,但是考虑到比特现金脚本语言将会继续发展,部署一个像group这样的功能是可以的。因此,在这部分内容里,我有时会使用当前不存在的数据源,以及使用尚不存在的操作码opcode PUSH_TX_DATA来定义它们。执行这个操作码可以访问当前的交易数据,并将其压入堆栈。


有期限的票证或优惠券

一个Group里的所有代币都是同一优惠券的实例。10%折扣的优惠券是与20%的是不同的group。购买者发起支付的时候,代币作为支付的一部分,用于激活优惠券。理想状态下,用代币构造交易,支付者可以先给商家提供tx, 这样商家可以验证优惠券是否正确。这个协议是对现有支付协议作出的一个很简单的扩展,但这不在本文讨论范围内。

优惠券一旦过期就毫无价值。优惠券应该在过期时,“某人可以花掉”,以便清零。优惠券的发出者可以扫描他的Group,一旦发现过期的优惠券,便将其全部清除,可以让所有者的钱包自己完成,或是让矿工利用未填满的区块空间做这件事情。

 

def couponExpiryTemplate(userAddress)

{

OP_IF  

  p2pkh(userAddress)

OP_ELSE  

  couponExpiryTime  

  OP_CHECKLOCKTIMEVERIFY  

  OP_TRUE // Anyone can spend if coupon is expired

OP_ENDIF


注意,上述代码只允许优惠券所有者使用p2pkh脚本转账。但是如果我们有OP_EXEC,我们可以移除这条要求,让所有者提供任意脚本:

 

def couponExpiryTemplate(userScript)

{

OP_IF  

  userScript  

  OP_EXEC 

OP_ELSE  

  couponExpiryTime  

  OP_CHECKLOCKTIMEVERIFY  

  OP_TRUE// Anyone can spend if coupon is expired

OP_ENDIF



备兑认购期权

备兑认购期权是一种以出售持有股票背书的期权。例如Alice一共有100股XYZ的股票,价格是10mBCH, 她觉得股票很稳定,于是把100股XYZ股票下个月到期的期权以15mBCH的价格出售。Bob认为XYZ的股票下个月会涨到30mBCH,于是他买入了Alice的call, 价格是每股1mBCH。如果Alice是正确的,Bob无法行权,Alice将保留100股XYZ股票和100mBCH。如果Bob是正确的,他行使权力购买Alice的股票,那么Alice将获得1500mBCH,Bob获得100股XYZ股票,现价是每股30mBCH,并扣除1500mBCH。

基本的方法是,首先锁定在脚本里锁定代币,只能在期货到期,买方行权的时候可以花费。创建一下个group,代表这个期权。它可以正常转账,但是也可以结合锁定期权的代币和支付交易来执行。

我们先创建GroupT,发行代币N。接着创建一个供应量有限的受限制Group,代表期权(OptT)。

接着,代币(期权可以用于交易)被发送至一个holding脚本,这个脚本可以使当前所有者在期权到期前都不能动用代币,但是允许执行Call。


def coveredCallLock(ownerAddress)

{  

  OP_IF // stop current owner from spending until releasedate    

          callExpiryTime    

          OP_CHECKLOCKTIMEVERIFY   

          p2pkh(ownerAddress)  

OP_ELSE // allow spend ifexercised    

          PUSH_TX_DATA(quantity of OptT ininputs)    

          P_DUP // We’ll use the abovetwice.    

          PUSH_TX_DATA(quantity of T in inputs) 

     OP_EQUALVERIFY // don’t allow T to bespent unless an equal number of options exist    

          PUSH_TX_DATA(quantity of OptT melted)   

          OP_EQUAL // make sure all options areconsumed.  

  OP_ENDIF


接着,发行期权代币N,并将它们发送至如下脚本。注意,在这个例子中,我使用的是更为普遍的userscriptOP_EXEC构想,接受者通过任意脚本都可以进行基本的转账。但是,如果不能使用OP_EXEC,该系统可能会限制转账,只能转到某一类型的地址上(例如P2PKH)。如果是这种情况,在代码里用“p2pkh(userPubKeyHash)”替换“userscript OP_EXEC”脚本有3字句,这三个字句由花费脚本通过把1s或者0s压入堆栈的标准方法选出。第一个子句只允许代币转账。第二个子句允许Bob行权,使用现在不存在的交易访问操作码,以确保交易包含支付给Alice的适当金额。第三个子句,Alice可以在到期的时候拿回期权。这个子句不需要很严格,因为基于当前的脚本,期权到期后Alice也可以将她的代币T转至新地址。


def coveredCallForToken(tokenHolderAddress,exercisePrice,

@userScript)

{

OF_IF  

  OP_IF // Option is transferred   

    @userScript   

    OP_EXEC  

OP_ELSE // Bob exercises theoption   

    PUSH_TX_DATA(quantity of this output)   

    exercise price   

    OP_MUL // figure out the total exercisecost   

    PUSH_TX_DATA(quantity of BCH sent totokenHolderAddress)   

    OP_EQUALVERIFY // Make sure exercise price of BCHhas been sent to alice   

    @userScript // Make sure Bob (current owner) is theone exercising   

    OP_EXEC  

  OP_ENDIF

OP_ELSE // After some time, Alice can take theoption away  

  callExpiryTime  

  OP_CHECKLOCKTIMEVERIFY 

  p2pkh(tokenHolderAddress) 

OP_ENDIF 


这实现了一个在到期前任意时间执行的期权。应该明确的是,在运行子句里的OP_CHECKLOCKTIMEVERIFY限制可以防止期权在特定时间之前执行。这与逾期日相结合,期权需要在这段特定的时间内执行。例如,30天后,Bob有1天的时间行权。

通过花费期权Group的mint authority,Alice可以证明她不会发行超过当前covering代币数量的期权,因为Group的铸币权限已经不存在。Alice不能使用相同的covering代币创建多个Group,因为期权Group识别符被编入coveredCallLock脚本。

这些脚本是为了说明一个概念,而不是明天就去部署。例如,它们的一个问题是,执行代码需要期权input的数量完全与代币input数量相匹配。但是,UTXO可能没法实现。例如,只有1个代币UTXO,代表100股,但是你只有50股的期权。鉴于这个问题是已知的,这是不可避免的;Alice创建了一定量的多个UTXO,Bob知道他必须购买多个UTXO。但是,要扩展脚本验证适量的“更改”output是否存在于给定的PUSH_TX_DATA当中也不会太难,指令类似于之前已经使用过的那些。

 

预测市场(投注代币化)

目前正在研究当中的OP_DATASIGVERIFY操作码可以构建一个赌注,投注的结果取决于受信任的oracles所做的陈述。但是,投注是单个时间点可能事件的快照。它无法捕捉到对于不同结果可能性的观点变化。预测市场允许人们购买和出售某个赌局结果的份额。不同结果的份额价格改变实际上反映的是对某一特定事件发生可能性的预测,这就是一个“预测市场”。

要创建一个预测市场,我们将采取以下步骤:首先,创建代表赌注结果份额的Group。接着,发送BCH到一个脚本里,如果相同数量的赢的份额被注入交易(这称之为储备资金BCH),这个脚本只能被花费。接着,如果结果是有利的,结果Group的mint token可以无限制转账,或是1:1兑换BCH储备金。最终,放弃结果Group的铸币权限,以证明将会有1:1的储备金做担保。

为了让事情简单化,我们将研究只有两种结果A和B的投注。使用OP_DATASIGVERIFY不在本文的讨论范围之内,但是它所支持的功能子集可以用以下的宏表示: 

makeBet(outcomePredicate, AWinsScript, BWinsScript, @data, @dataSignature)

MakeBet首先检查oracle是否已经对所提供的数据进行适当的签名。接着使用这个数据决定output A或者B。如果是A, 执行AWinsScript,如果是B,则执行BWinsScript。

我们将会创建2个代币Group A和B,这代表这个预测市场可交易的份额。AWinsScript和BWinsScript代码如下:

def WinsScript(tokGrp)

{     

        // You can’t spend this unlessyou are spending the same quantity of tokGrp (A or B)     

     PUSH_TX_DATA(quantity of BCHinputs)      

     PUSH_TX_DATA(quantity of tokGrpinputs)     

     OP_EQUALVERIFY // verify BCHinput quantity == tokGrp quantity                    

     // Verify that the spender ismelting the tokGrp units not transferring them     

     PUSH_TX_DATA(quantity of tokGrpoutputs)       

     OP_0      OP_EQUAL     

     // Optionally, you could add aOP_CHECKLOCKTIMEVERIFY clause that spends the coins back to yourself aftergiving people sufficient time to redeem their A and B tokens.  This would clean up after forgetful people, andgive you an exit without loss if for some reason the bet is undecidable or theoracle disappears.


BCH需要打包入一个“赌注”group,以区别于代币转账时发送的BCH。

makeBet(predicate, WinsScript(A), WinsScript(B)) 

示例使用的是BchScript macro语言,这个语言可以用关键字“def”对子程序进行定义。当子程序随后在代码里相遇,它将由定义内容替代(类似于C’s #的定义)。注意,因为编译程序只简单使用macro replacement,参数可能是数据或代码。这个语言还定义了一个特殊的语法,这是由于比特币脚本执行的独特方式:如果一个变量开始以@符号开头,那么它是由支出(签名)脚本提供的一个预先存在于堆栈上的参数。这二者都有助于提高可读性,

还要注意,我们确保这笔交易里的奖励代币已消耗(销毁)。(Melt Authority input通常都可以用作为input)这保证代币不会被多次兑现。

我们现在按照如下encumbrance脚本模板创建Group A 和B:

Def winsToken(grpId, @userScript)

{   

    OP_IF  // case where the token is transferred    

    // New output script    

       @userScript    

    OP_EXEC  

  OP_ELSE  // case where the bet isresolved, redeeming    

    // Make sure spender is claimingthe right number of “Bet” tokens    

        PUSH_TX_DATA(quantity of BCH inputs)    

        PUSH_TX_DATA(quantity of grpIdinputs)     

     OP_EQUALVERIFY    

     // make sure spender is meltinghis claims         

     PUSH_TX_DATA(quantity of outputgrpId)    

     OP_0    

     OP_EQUALVERIFY    

     // Enforce ownership of thisclaim    

     @userScript    

     OP_EXEC   

  OP_ENDIF

}

再次说明,如果用户提供自己的BCHinput,那么他可以成功兑现(OP_ELSE子句)。在这种情况下,这个脚本允许他“兑现”他已拥有的BCH,销毁程序里的资金。但理性的用户只会纳入受限于“makeBet”脚本里的BCH output,因为持有他认领的代币,他可以花费这笔output,从而获取这笔BCH。

这个例子说明,在比特币脚本和共识里增加基本操作——通过oracles、代币(可互换性界限),以及让脚本访问数据——可以组合实现很复杂的操作。

 

附录

附录A:示例代码说明

示例使用BchScript macro语言,子程序可以用关键字“def”定义。当子程序随后在代码里相遇的时候,它将由定义内容替代(类似于C’s #的定义)。注意,因为编译程序只简单使用macro replacement,参数可能是数据或代码。这个语言还定义了一个特殊的语法,这是由于比特币脚本执行的独特方式:如果一个变量开始以@符号开头,那么它是由支出(签名)脚本提供的一个预先存在于堆栈上的参数。这二者都有助于提高可读性,并实现在编译时从代码中生成所有可能的spend脚本。例如,这个代码定义普通的P2PKH脚本如下:


def p2pkh(pubkeyhash, @sig, @pubkey)   

    {  

    @sig  

    @pubkey  

    OP_DUP  

    OP_HASH160  

    pubkeyhash  

    OP_EQUALVERIFY  

    OP_CHECKSIG  

}


一个标准的P2PKH output将会包含对这个宏的单独调用:

p2pkh(bitcoincash:qpae0tg5k4mh5uhhcnwkx2rytj3800j8nunzm8rmed)

这是一个非常简短的说明,但应该足以读懂示例的伪代码。


附录B:合法性说明

当你读到后面的Group使用场景时,你可能会想,“这不符合法律,所以我们不应该这么做”。但是这个推理是有问题的。对于这个论断最有力的证明是:这个世界有比特币、比特现金、Airbnb、优步、Lyft、Kickstarter,以及很多成功的社会变革技术和企业,他们都处于法律的灰色地带或完全被禁止(并且通常都会解禁)。让我们共同探究里面的原因,但要知道,我不是一名律师。


法律具有复杂性、矛盾性和地域性

人们,尤其是软件开发者对于法律的认识都过于简单粗暴。新兴技术和新立法的实际情况要远远复杂得多。新兴技术和新立法要复杂太多,要做出决定必须有真实的法庭审理案件作为参考,即要考虑双方的意见,又要查询是否有先例和考虑诉讼程序。

美国的一个经典案例是焚烧国旗案。该案件始末https://en.wikipedia.org/wiki/Texas_v._Johnson 在最高法院作出最终判决之前,案情一直在反转。随后国会通过了1989年《国旗保护法》,但不久被取消。这还没结束,国会随后又多次试图采取这项法案,但到现在都没能成功。

焚烧国旗事件也展示了法律体系的另外一面。1989年《国旗保护法》生效没多久,就有人在联邦大楼面前蓄意破坏这项法律。尽管有很多先例和道德判断供你预测你某种行为的后果,但遗憾的是,如果存在模棱两可,要确定法律的有效性,以及是否适用于你身上的唯一方法就是行动,然后看看会发生什么。这就是为什么有人会在这项法律刚生效的时候焚烧国旗。结果是在美国诉艾奇曼一案中https://en.wikipedia.org/wiki/United_States_v._Eichman,再次取消该法。这暂时“解决”了一个国家的这一个问题,全球还有195个国家。

在美国,很多金融问题都是由州政府监管。这意味着,对于比特现金和代币化的问题,仅在美国就有50个不同的司法辖区。

纽约的数字货币许可证(BitLicense )就是这种情况的一个好例子。确定BitLicense 合法性的唯一途径就是挑战它,Theo Chino就是这么干的(https://cryptoinsider.com/content/ny-oct-10-court-challenge-may-worldwide-bitcoin-implications/index.html)。

Creative Legal Interpretation Can Make It Possible

非常成功并且具有社会价值的初创企业例如优步、Lyft、Kickstarter 和Indegogo都是游走在法律的灰色地带。以前使用Lyft叫车是免费的,乘客可以选择性捐款。因此,这不是合法的出租车(交通运输)服务。但是如果你不“捐赠”,就别指望还会有Lyft司机愿意载你。

在众筹方面,SEC制定了一条法规将最高100万美元的捐款合法化(https://www.sec.gov/news/pressrelease/2015-249.html),但这样的限制立刻被无视((https://www.kickstarter.com/projects/poots/kingdom-death-monster-15/description)。然而,这说明了法律另外一个很重要的现实点:作出判决或是改变需要非常充分的理由(例如$$$)。监管一切可能会发生的事情,将会耗掉有关部门太多时间,但是如果事情已经发生,想象后果要容易得多。

搬到加密货币上来,很多ICO谨慎发行代币,以避开豪威测试和证券法。(https://consumer.findlaw.com/securities-law/what-is-the-howey-test.html)避开豪威测试一个常见的方法是避开第三点。“普通企业”指的是你的公司。但是如果代币被用于一个开源且无需许可的网络(这个网络刚好目前由你的公司维护)和两个团体之间的中介服务(你的公司刚好是最大的服务供应商),那么从法律上来说,购买该代币有可能不是对某个公司进行投资。

让我们发散思维,看一下注册证券。注册证券要求公司在账簿中记录证券持有人的信息,以便派发股息、记录选票和交流。要转让股份,持股人有义务告知公司(否则这是不合法的)。从表面面来看,只使用区块链代币,公司是不可能做到这点的,因为代币可以点对点转移。我们需要额外增加一个步骤,把转移信息发送给公司。

但是,区块链的交易记录可以作为发给公司的通知?实际上,区块链是否可以作为公司的账本?你可以用BCH向持有代币的相同output脚本(或地址)支付股息。你可以同样通过构建交易在OP_RETURN字段附上文件(或链接)与持有人沟通。你可以让持有人用代币output地址对应的私钥对声明进行签名,以此进行投票。与传统的方式相比,这些技术里每一项都有优势,其中包括“三式记账法”、可证实的沟通、可证实的股息支付,以及可证实的投票。

技术不会执行法律

汽车会拒绝超速吗?他们应该执行公路法吗?如果有高速路规定最低行驶速度,那自动驾驶如何处理那些低于下限速度的汽车呢?显然这个问题很愚蠢,但法律如何加强对新技术(自动驾驶)的执法,但对其他(非新技术)的执法呢?谁来决定执法?

我们根本不能预先确定一些普通概念,例如“链上证券”的各种具体表现形式在全球数百个司法管辖区都是非法的。作为工程师,我们必须用技术创造出我们想要生活的世界,并相信法律会被明确或修订,但在该技术受到监管的管辖区,最终依赖于个人的知情选择。

 

附录C:OP_RETURN里的Group信息

OP_RETURN output的内容如下:


`<88888888> <ticker> <0> <name><0> <URI> <0> <dochash>` 


有可能会有空白的字段,例如:


`<88888888>MYCOIN\0\0http://www.mycoin.com/info` 


字段定义<88888888>:根据这里提供指导,在OP_RETURN里定义这类数据的一个4字节数字。


<ticker>: 代币简称,显示在货币数量附近。最大8个字符。

<name>: 代币全称

<URI>: 这个字段支持http和https协议,请参考以下代币说明文档里的内容"application/json" type document in [I-JSON format]

(https://tools.ietf.org/html/rfc7493)

<dochash>: 对代币说明文档里的字典做两次SHA256。这个哈希值提交至文档内容并证明它没有被修改过。

 

代币说明文档

代币说明文档是一个使用I-JSON格式的“application/json”类型的文档(https://tools.ietf.org/html/rfc7493)至少包含以下字段。如有需要,作者可以纳入其他字段。

**signature**: 这个签名字段包含使用所有需要花费第一个group authority的签名的前一个字典的签名。验证者必须检查这个文件的确切字节数,保证行距没有变。签名算法与在Satoshi客户端的signmessage RPC函数里使用的相同。以下信息仅供参考,不具有权威性:

*通过使用Bitcoin Signed Message:\n字符串的double SHA-256计算信息哈希值+信息*创建一个致密的ECDSA签名*使用base 64编码将下面的字符集转换成文本:

"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

钱包和用户使用这个签名表明代币的创建者正在确认这个json文档里的信息。这个签名也证明这个文档和参考URL与这个Group是相关的。通过host他的文件,域名所有者与代币关联。这就是为什么域名是XYZ.com的XYZ公司发行的代币与假冒的XYZ代币不同。

建议代币发行方为统一资源识别符使用https协议,这样就无法发动中间人攻击,使其看起来像是你的域名正在host一份代币描述文档。


**ticker**: 代币简称。最大8个字符。

**name**: 代币全称

**summary**: 段落描述

**description**: 完整描述

**legal**: 代币用户和代币发行者之间完整的合法合约,如果有的话。

**creator**: 代币创建者

**contact**: 任选的创建者联系信息,例如:

"contact": { "email": "satoshin@gmx.com" }

定义的方法是: "address", "email", "phone","twitter.com", "facebook.com"

 

作者可能会添加自己的方法。如果这是一个支持web的服务,请使用域名来标识该方法。如果这是一个URL协议,则使用包含以冒号结束的协议名称(e.g. "http:": "//www.mywebsite.com/contactform")

作者可能会添加这个字典这个文件里没有定义的字段。

 

不合法的代号名称

因为OP_RETURN没有达成共识,代币发行方可以任意命名。但是,为了避免用户混淆,钱包可能会限制用户使用与现有货币或证券有密切关联的名字。因为这些名字可能会经常换,所以更好的方案是以协商一致的方式命名。钱包作者可以保存一张预留的名单,并定期更新该名单的副本。

钱包的作者可能会考虑要求在显示代币简称之前进行人工审批。例如,收到一个新型的代币,钱包可以问: “Display <fullname> issued by <domain name of URI> as<short name>?” 

但如果代币声称要使用下面的代码(不区分大小写),我们强烈建议钱包拒绝显示这些代码,而是使用hex里组识别符的前几个字节:

BCH, mBCH, uBCH, cash 

如果一个代币表示要使用下面的代码(不区分大小写),我们将以钱包拒绝显示该代码(使用hex里组识别符的前几个字节),除非用户明确指定:

Any ISO4217 currency code,

any NYSE ticker,

any NASDAQ ticker,

any symbol from your locale's national securitiesexchange,

other popular crypto-currencies. 


如果第三方公司或个人正在创建一个代表证券的代币,他们的代码应该反映出,这个代币是“具有代表性的”。例如,如果公司ABC代表股东持有证券XYZ,代币可以命名为ABC.XYZ。

在比特现金的区块链上发行真正的货币和证券是有可能的。我们保留国家和国际知名的股票代码,以备将来使用,但目前用户可以忽略这点,因为钱包实现很有可能会落后于最早的一些部署。

原文pdf下载: 链接: https://pan.baidu.com/s/1bV62pMg4yJLFIsij_qA7sw 密码: 4ucn

 


(欢迎加我微信号:HSL13116885 加入我的知识星球)







另请阅读:

《在比特币上发代币的基本原理——omni协议发代币的通俗解释》

上一篇:

《发币容易,护盘不易,且炒且珍惜——探索最好的代币经济模型》

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存